package com.gao.security.verificationcode.filter;

import com.gao.security.verificationcode.VerficationCodeManager;
import com.gao.security.verificationcode.base.BaseCode;
import com.gao.security.verificationcode.base.VerficationCode;
import com.gao.security.verificationcode.common.SecurityConstants;
import com.gao.security.verificationcode.event.ValidateCodeRemoveEvent;
import com.gao.security.verificationcode.exception.ValidateCodeIncorrectException;
import com.gao.security.verificationcode.exception.ValidateCodeNotFoundException;
import com.gao.security.verificationcode.exception.ValidateCodeTimeoutException;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 验证码过滤器
 * </p>
 *
 * @author GaoShuangWen
 */
public class ValidateCodeAuthenticationFilter extends
        OncePerRequestFilter implements ApplicationContextAware {
    private static final Logger log = LoggerFactory.getLogger(ValidateCodeAuthenticationFilter.class);
    private List<RequestMatcher> requiresAuthenticationRequestMatchers = Lists.newArrayList();
    private ApplicationContext applicationContext;
    private VerficationCodeManager verficationCodeManager;
    private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
    public ValidateCodeAuthenticationFilter() {

    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (!requiresAuthentication(request)) {
            filterChain.doFilter(request, response);
            return;
        }
        String key = request.getHeader(SecurityConstants.VALIDATE_CODE_KEY);
        String code = request.getParameter(SecurityConstants.VALIDATE_CODE);
        if (key == null || code == null) {
            log.debug("ValidateCode not found");
            ValidateCodeNotFoundException v = new ValidateCodeNotFoundException("ValidateCode not found");
            failureHandler.onAuthenticationFailure(request, response, v);
            throw v;
        }
        ValidateCodeRemoveEvent event = new ValidateCodeRemoveEvent(key);
        try {
             verficationCodeManager.verification(new VerficationCode() {
                 @Override
                 public String getCode() {
                     return code;
                 }
                 @Override
                 public String getUuid() {
                     return key;
                 }
             });
            applicationContext.publishEvent(event);
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            if (e instanceof ValidateCodeTimeoutException) {
                applicationContext.publishEvent(event);
                failureHandler.onAuthenticationFailure(request, response, (ValidateCodeTimeoutException) e);
            } else if (e instanceof ValidateCodeIncorrectException) {
                failureHandler.onAuthenticationFailure(request, response, (ValidateCodeIncorrectException) e);
            } else {
                throw e;
            }
        }

    }

    private boolean requiresAuthentication(HttpServletRequest request) {
        return requiresAuthenticationRequestMatchers.stream()
                .anyMatch(requestMatcher -> requestMatcher.matches(request));
    }

    public void setVerficationCodeManager(VerficationCodeManager verficationCodeManager) {
        this.verficationCodeManager = verficationCodeManager;
    }

    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();
       /* ValidateCodeProperties bean = applicationContext.getBean(ValidateCodeProperties.class);
        // 获取配置的验证码请求
        List<String> url = bean.getUrl();
        if (url != null) {
            url.forEach(u -> requiresAuthenticationRequestMatchers.add(new AntPathRequestMatcher(u, "POST")));
        }*/
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public void setFailureHandler(AuthenticationFailureHandler failureHandler) {
        this.failureHandler = failureHandler;
    }
}
